package com.luntan.dao;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;

import com.luntan.util.web.Page;

/**
 * 数据库访问层实现层
 *
 * @author MaShiQiong
 * @date 2016-11-14 14:14:27
 * @param <T>
 * @param <K>
 */
public class BaseDAOImpl<T, K extends Serializable> implements BaseDAO<T, K> {

	private SessionFactory sessionFactory;
	private DataSource dataSource;

	/**
	 * 加载实体
	 */
	@Override
	public T getEntity(Class<T> clasz, K k) throws Exception {
		return (T) this.sessionFactory.getCurrentSession().get(clasz, k);
	}

	/**
	 * 加载实体
	 */
	@Override
	public T loadEntity(Class<T> clazz, K k) throws Exception {
		return (T) this.sessionFactory.getCurrentSession().load(clazz, k);
	}

	/**
	 * 通过查询语句来获取实体对象
	 *
	 * @param hsql
	 * @return
	 * @throws Exception
	 */
	@Override
	public T getEntity(String hsql) throws Exception {
		List<T> list = this.findEntity(hsql);
		if (list != null && list.size() > 0) {
			return list.get(0);
		} else {
			return null;
		}
	}

	/**
	 * 保存实体
	 */
	@Override
	public T saveEntity(T t) throws Exception {
		Session currentSession = this.sessionFactory.getCurrentSession();
		currentSession.save(t);
		//currentSession.flush();
		return t;
	}

	/**
	 * 保存临时实体
	 */
	@Override
	public T saveTempEntity(T t) throws Exception {
		Session currentSession = this.sessionFactory.getCurrentSession();
		currentSession.save(this.prepObject(t));
		//currentSession.flush();
		return t;
	}

	public T prepObject(T t) {
		return (T) this.sessionFactory.getCurrentSession().merge(t);
	}

	/**
	 * 更新实体
	 */
	@Override
	public T updateEntity(T t) throws Exception {
		Session currentSession = this.sessionFactory.getCurrentSession();
		currentSession.update(t);
		//currentSession.flush();
		return t;
	}

	/**
	 * 根据hql语句更新
	 */
	@Override
	public void updateEntity(final String hql) throws Exception {
		this.sessionFactory.getCurrentSession().createQuery(hql).executeUpdate();
	}

	/**
	 * 添加或保存实体
	 */
	@Override
	public T saveOrUpdate(T t) throws Exception {
		Session currentSession = this.sessionFactory.getCurrentSession();
		currentSession.saveOrUpdate(t);
		//currentSession.flush();
		return t;
	}

	/**
	 * 添加或保存实体集合
	 */
	@Override
	public void saveOrUpdateColl(Collection<T> co) throws Exception {
		Session currentSession = this.sessionFactory.getCurrentSession();
		for (Object entity : co) {
			currentSession.saveOrUpdate(entity);
		}
		//currentSession.flush();
	}

	/**
	 * 删除实体集合
	 */
	@Override
	public void removeAllEntity(Collection<T> obj) throws Exception {
		Session currentSession = this.sessionFactory.getCurrentSession();
		for (Object entity : obj) {
			currentSession.delete(entity);
		}
		//currentSession.flush();
	}

	/**
	 * 删除实体
	 */
	@Override
	public void removeEntity(T t) throws Exception {
		Session currentSession = this.sessionFactory.getCurrentSession();
		currentSession.delete(t);
		//currentSession.flush();
	}

	/**
	 * 通过hql语句查找实体集合
	 */
	@Override
	public List<T> findEntity(String hql) throws Exception {
		return this.sessionFactory.getCurrentSession().createQuery(hql).getResultList();
	}

	/**
	 * 通过hql语句查找实体集合
	 */
	@Override
	public List<T> findEntity(String hql, Object obj) throws Exception {
		return findEntity(hql, new Object[] { obj });
	}

	/**
	 * 通过hql语句查找实体集合
	 */
	@Override
	public List<T> findEntity(String hql, Object[] obj) throws Exception {
		Session session = this.sessionFactory.getCurrentSession();
		Query<T> query = session.createQuery(hql);
		if (obj != null) {
			for (int i = 0; i < obj.length; i++) {
				query.setParameter(i, obj[i]);
			}
		}
		return query.getResultList();
	}

	/**
	 * 根据hql查询 返回Object
	 */
	@Override
	public List<Object> findList(String hql) throws Exception {
		return this.sessionFactory.getCurrentSession().createQuery(hql).getResultList();
	}

	/**
	 * 根据hql查询 返回Object
	 */
	@Override
	public List<Object> findList(String hql, Object obj) throws Exception {
		return this.findList(hql, new Object[] { obj });
	}

	/**
	 * 根据hql查询 返回Object
	 */
	@Override
	public List<Object> findList(String hql, Object[] obj) throws Exception {
		Query<Object> query = this.sessionFactory.getCurrentSession().createQuery(hql);
		for (int i = 0; i < obj.length; i++) {
			query.setParameter(i, obj[i]);
		}

		return query.getResultList();
	}

	/**
	 * 根据hql查询 返回Object数组
	 */
	@Override
	public List<Object[]> findListArray(String hql) throws Exception {
		return this.sessionFactory.getCurrentSession().createQuery(hql).getResultList();
	}

	/**
	 * 根据hql查询 返回Object数组
	 */
	@Override
	public List<Object[]> findListArray(final String hql, final int start, final int limit) throws Exception {
		Query<Object[]> query = this.sessionFactory.getCurrentSession().createQuery(hql);
		query.setFirstResult(start).setMaxResults(limit);
		return query.getResultList();
	}

	/**
	 * 通过hql语句查找实体集合
	 */

	@Override
	public List<T> findEntity(final String hql, final int start, final int limit) throws Exception {
		Query<T> query = this.sessionFactory.getCurrentSession().createQuery(hql);
		query.setFirstResult(start).setMaxResults(limit);
		return query.getResultList();
	}

	/**
	 * 根据sql语句查询
	 */
	@Override
	public List<Map<String, Object>> findListBySql(final String sql) throws Exception {
		Statement statement = this.getConnection().createStatement();
		ResultSet resultSet = statement.executeQuery(sql);

		ResultSetMetaData rsmd = resultSet.getMetaData();
		int numberOfColumns = rsmd.getColumnCount();
		List<Map<String, Object>> listOfRows = new ArrayList<Map<String, Object>>();
		while (resultSet.next()) {
			Map<String, Object> mapOfColValues = new LinkedHashMap<String, Object>(numberOfColumns);
			for (int i = 1; i <= numberOfColumns; i++) {
				mapOfColValues.put(rsmd.getColumnName(i).toLowerCase(), resultSet.getObject(i));
			}
			listOfRows.add(mapOfColValues);
		}
		resultSet.close();
		return listOfRows;

	}

	/**
	 * 根据sql语句执行
	 */
	@Override
	public void executeSQL(final String sql) throws Exception {
		this.sessionFactory.getCurrentSession().createNativeQuery(sql).executeUpdate();
	}

	/**
	 * 分页查询
	 */
	@Override
	public Page<T> search(String hql, int page, int limit) throws Exception {
		return this.searchForPager(hql, limit, page);
	}

	/**
	 * 分页实现方法
	 *
	 * @param hql
	 * @param limit 每页大小
	 * @param page 当前页
	 * @return 分页对象
	 */
	private Page<T> searchForPager(String hql, int limit, int page) throws Exception {
		int maxPage;
		List<T> results;
		List<Integer> pageNum = new ArrayList<Integer>();

		// sql数据库需要去除order by
		String thql = null;
		if (hql.indexOf("order by") == -1) {
			thql = "select count(*) " + hql;
		} else {
			thql = "select count(*) " + hql.substring(0, hql.indexOf("order by"));
		}

		int count = Integer.valueOf(this.findEntity(thql).get(0).toString());

		// 计算页数
		if (count % limit == 0) {
			maxPage = count / limit;
		} else {
			maxPage = count / limit + 1;
		}

		if (maxPage == 0) {
			maxPage = 1;
			page = 1;
		} else {
			page = page <= maxPage ? (page > 0 ? page : 1) : maxPage;
		}

		// 页码
		if (page < 6) {
			for (int i = 1; i < 10; i++) {
				if (i <= maxPage) {
					pageNum.add(i);
				} else {
					break;
				}
			}
		} else {
			for (int i = page - 4; i <= page + 4; i++) {
				if (i <= maxPage) {
					pageNum.add(i);
				} else {
					break;
				}
			}
		}
		results = this.findEntity(hql, (page - 1) * limit, limit);
		Page<T> p = new Page<T>(page, maxPage, count, pageNum, results);
		return p;
	}

	@Override
	public Connection getConnection() throws SQLException {
		return this.dataSource.getConnection();
	}

	/**
	 * @param sessionFactory the sessionFactory to set
	 */
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	/**
	 * @param dataSource the dataSource to set
	 */
	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}
}
